Terraformで、同じ構成を複数プロビジョニングしたい: Module編
先日 HashiTalks Japanで「シングルテナント構成のSaaSのIaCにTerraform Workspacesを導入してみた」というビデオ登壇をしました。その中で時間の都合でご紹介できなかった、「Workspacesを使う以外の、同じ構成(リソースセット)を複数個プロビジョニングする方法案」を、複数回に分けてご紹介していきます。
関連エントリ
- #HashiTalks Japanで「シングルテナント構成のSaaSのIaCにTerraform Workspacesを導入してみた」という発表をしました
- Terraformで、同じ構成を複数プロビジョニングしたい: Module編 ? イマココ
- Terraformで、同じ構成を複数プロビジョニングしたい: ディレクトリ分割編
- Terraformで、同じ構成を複数プロビジョニングしたい: Terragruntでrun-all編
- Terraformで、同じ構成を複数プロビジョニングしたい: backend-configオプション編
- Terraformで、同じ構成を複数プロビジョニングしたい: Terragruntでbackendを動的設定編
今回はモジュールを使うパターンのご紹介です。
詳細
dest_aとdest_bという展開先があるとします。
. ├── modules │ └── base │ ├──・ │ └──・ ├── dest_a.tf ├── dest_b.tf ├── outputs.tf ├── variables.tf └── versions.tf
provider "aws" { alias = "dest_a" assume_role { role_arn = "arn:aws:iam::111111111111:role/terraform" } } module "dest_a_base" { source = "./modules/base" providers = { aws = aws.dest_a } }
provider "aws" { alias = "dest_b" assume_role { role_arn = "arn:aws:iam::222222222222:role/terraform" } } module "dest_b_base" { source = "./modules/base" providers = { aws = aws.dest_b } }
dev/stg/prodのように複数環境が必要な場合は以下のようにし、 env/(dev|stg|prod)
で各種terraform コマンドを実行します。
. ├── env │ ├── dev │ │ ├── dest_a.tf │ │ ├── dest_b.tf │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── versions.tf │ ├── prod │ │ ├── ・ │ │ └── ・ │ └── stg │ ├── ・ │ └── ・ └── modules └── base ├── ・ └── ・
プロビジョニングするリソースの数が多い場合はモジュールを複数個に分けることも考えましょう。
. ├── env │ ├── dev │ │ ├── dest_a.tf │ │ ├── dest_b.tf │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── versions.tf │ ├── prod │ │ ├── ・ │ │ └── ・ │ └── stg │ ├── ・ │ └── ・ └── modules ├── app │ ├── ・ │ └── ・ ├── db │ ├── ・ │ └── ・ ├── network │ ├── ・ │ └── ・ └── web ├── ・ └── ・
この構成の良い点
シンプル
一度のterraform apply
だけで全展開先にプロビジョニングできるのでシンプルです。CDパイプラインを作る際もシンプルにできるでしょう。
展開先間の差分に対応しやすい
共通部分をモジュール化しているだけなので、展開先間で差分がある場合に対応しやすいです。
使うモジュールを変える
Aではsubモジュールを使うけどBでは使わない例です。
module "dest_a_main" { source = "./modules/main" providers = { aws = aws.dest_a } } module "dest_a_sub" { source = "./modules/sub" providers = { aws = aws.dest_a } }
module "dest_b_main" { source = "./modules/main" providers = { aws = aws.dest_b } }
variableを使ってモジュール内の挙動を変える
module "dest_a_base" { source = "./modules/base" providers = { aws = aws.dest_a } enable_nat_gateway = true }
module "dest_b_base" { source = "./modules/base" providers = { aws = aws.dest_b } enable_nat_gateway = false }
直接リソース書いてしまっても良い
module "dest_a_base" { source = "./modules/base" providers = { aws = aws.dest_a } } resource "aws_s3_bucket" "dest_a" { bucket_prefix = "dest_a" }
この構成のイマイチな点
実行時間が長くなる
メリットに書いた「一度に全展開先にプロビジョニングできるシンプルさ」と引き換えに、planやapplyなど各terraformコマンドの実行時間は長くなります。そのため展開先が非常にたくさんある場合には適さないでしょう。
この構成を採る場合は、並列実行数を上げてterraformコマンド実行を高速化することを検討するのが良いでしょう。
展開先の情報がコードに出る、Gitに残る
ここまでご紹介したサンプルコードがそうであるように、展開先毎にproviderの設定とmodule呼び出しのコードを書く必要があります。通常それはGitを始めとするVCSに記録されるでしょう。(何らかの方法で動的にコード生成すれば回避できるかもしれませんが…)
要件次第ではこれは好ましいことですが、そうでない場合もあるでしょう。例えばシングルテナント構成のSaaSを作るとなった場合、テナントの情報をコードやGitに残すことはしたくないと思います。
ユースケース
DRとして、メインリージョンとは別にホットスタンバイで別リージョンにも同じリソースセットを用意したい、といった場合にはハマるんじゃないかと思います。あまり展開先が増えない要件が適していると言えるでしょう。